<!-- HTML header for doxygen 1.8.6-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=9">
<meta name="generator" content="Doxygen 1.8.13">
<title>OpenCV：如何使用OpenCV扫描图像、查找表和时间度量</title>
<link href="../../opencv.ico" rel="shortcut icon" type="image/x-icon">
<link href="../../tabs.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="../../jquery.js"></script>
<script type="text/javascript" src="../../dynsections.js"></script>
<script type="text/javascript" src="../../tutorial-utils.js"></script>
<link href="../../search/search.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="../../search/searchdata.js"></script>
<script type="text/javascript" src="../../search/search.js"></script>
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
    extensions: ["tex2jax.js", "TeX/AMSmath.js", "TeX/AMSsymbols.js"],
    jax: ["input/TeX","output/HTML-CSS"],
});
//<![CDATA[
MathJax.Hub.Config(
{
  TeX: {
      Macros: {
          matTT: [ "\\[ \\left|\\begin{array}{ccc} #1 & #2 & #3\\\\ #4 & #5 & #6\\\\ #7 & #8 & #9 \\end{array}\\right| \\]", 9],
          fork: ["\\left\\{ \\begin{array}{l l} #1 & \\mbox{#2}\\\\ #3 & \\mbox{#4}\\\\ \\end{array} \\right.", 4],
          forkthree: ["\\left\\{ \\begin{array}{l l} #1 & \\mbox{#2}\\\\ #3 & \\mbox{#4}\\\\ #5 & \\mbox{#6}\\\\ \\end{array} \\right.", 6],
          forkfour: ["\\left\\{ \\begin{array}{l l} #1 & \\mbox{#2}\\\\ #3 & \\mbox{#4}\\\\ #5 & \\mbox{#6}\\\\ #7 & \\mbox{#8}\\\\ \\end{array} \\right.", 8],
          vecthree: ["\\begin{bmatrix} #1\\\\ #2\\\\ #3 \\end{bmatrix}", 3],
          vecthreethree: ["\\begin{bmatrix} #1 & #2 & #3\\\\ #4 & #5 & #6\\\\ #7 & #8 & #9 \\end{bmatrix}", 9],
          cameramatrix: ["#1 = \\begin{bmatrix} f_x & 0 & c_x\\\\ 0 & f_y & c_y\\\\ 0 & 0 & 1 \\end{bmatrix}", 1],
          distcoeffs: ["(k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6 [, s_1, s_2, s_3, s_4[, \\tau_x, \\tau_y]]]]) \\text{ of 4, 5, 8, 12 or 14 elements}"],
          distcoeffsfisheye: ["(k_1, k_2, k_3, k_4)"],
          hdotsfor: ["\\dots", 1],
          mathbbm: ["\\mathbb{#1}", 1],
          bordermatrix: ["\\matrix{#1}", 1]
      }
  }
}
);
//]]>
</script><script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js"></script>
<link href="../../doxygen.css" rel="stylesheet" type="text/css">
<link href="../../stylesheet.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<!--#include virtual="/google-search.html"-->
<table cellspacing="0" cellpadding="0">
 <tbody>
 <tr style="height: 56px;">
  <td id="projectlogo"><img alt="Logo" src="../../opencv-logo-small.png"></td>
  <td style="padding-left: 0.5em;">
   <div id="projectname">OpenCV
   &#160;<span id="projectnumber">4.5.2</span>
   </div>
   <div id="projectbrief">Open Source Computer Vision</div>
  </td>
 </tr>
 </tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.13 -->
<script type="text/javascript">
var searchBox = new SearchBox("searchBox", "../../search",false,'Search');
</script>
<script type="text/javascript" src="../../menudata.js"></script>
<script type="text/javascript" src="../../menu.js"></script>
<script type="text/javascript">
$(function() {
  initMenu('../../',true,false,'search.php','Search');
  $(document).ready(function() { init_search(); });
});
</script>
<div id="main-nav"></div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow" onmouseover="return searchBox.OnSearchSelectShow()" onmouseout="return searchBox.OnSearchSelectHide()" onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>

<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0" name="MSearchResults" id="MSearchResults">
</iframe>
</div>

<div id="nav-path" class="navpath">
  <ul>
<li class="navelem"><a class="el" href="../../d9/df8/tutorial_root.html">OpenCV Tutorials</a></li><li class="navelem"><a class="el" href="../../de/d7a/tutorial_table_of_content_core.html">The Core Functionality (core module)</a></li>  </ul>
</div>
</div><!-- top -->
<div class="header">
  <div class="headertitle">
<div class="title">如何使用OpenCV扫描图像、查找表和时间测量</div>  </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><p><b>上一个教程:</b> <a class="el" href="../../d6/d6d/tutorial_mat_the_basic_image_container.html">Mat - The Basic Image Container</a></p>
<p><b>下一个教程:</b> <a class="el" href="../../d7/d37/tutorial_mat_mask_operations.html">Mask operations on matrices</a></p>
<table class="doxtable">
<tr>
<th align="right"></th><th align="left"></th></tr>
<tr>
<td align="right">原作者</td><td align="left">Bernát Gábor</td></tr>
<tr>
<td align="right">兼容性</td><td align="left">OpenCV &gt;= 3.0</td></tr>
</table>
<h2>目标</h2>
<p>我们将为以下问题寻求答案:</p>
<ul>
<li>如何遍历图像的每个像素？</li>
<li>如何存储OpenCV矩阵值？</li>
<li>如何衡量我们算法的性能？</li>
<li>什么是查找表？为什么使用它们？</li>
</ul>
<h2>我们的测试用例</h2>
<p>让我们考虑一种简单的色彩还原方法。通过使用 unsigned char C 和C++类型来存储矩阵项，像素的信道可以具有多达256个不同的值。对于三通道图像，这可能会形成太多的颜色（确切地说是1600万）。使用如此多的颜色阴影可能会严重影响我们的算法性能。然而，有时只需与很少的人一起工作就可以得到相同的最终结果。</p>
<p>在这种情况下，我们通常会<em>色彩空间缩小</em>. 这意味着我们将颜色空间的当前值除以一个新的输入值，以得到更少的颜色。例如，0到9之间的每个值取新值0，10到19之间的每个值取10，以此类推。</p>
<p>当你划分一个<em>uchar</em>(unsigned char-aka值介于0和255之间)带有<em>int</em>结果也将是<em>char</em>. 这些值只能是char值。因此，任何分数都将向下舍入。利用这一事实，在<em>uchar</em>域可以表示为：</p>
<p class="formulaDsp">\[I_{new} = (\frac{I_{old}}{10}) * 10\]</p>
<p>一个简单的颜色空间缩减算法将只通过图像矩阵的每个像素，并应用这个公式。值得注意的是，我们做除法和乘法运算。这些操作对于一个系统来说是非常昂贵的。如果可能的话，可以通过使用更便宜的操作来避免它们，比如一些减法、加法或者在最好的情况下一个简单的赋值。此外，请注意，对于上限操作，我们只有有限数量的输入值。以防<em>高的</em>这个系统准确地说是256。</p>
<p>因此，对于较大的图像，明智的做法是预先计算所有可能的值，并在赋值过程中使用查找表进行赋值。查找表是简单的数组（具有一个或多个维度），对于给定的输入值变量，它保留最终的输出值。它的优点是我们不需要计算，只需要读取结果。</p>
<p>我们的测试用例程序（以及下面的代码示例）将执行以下操作：读入作为命令行参数传递的图像（它可以是彩色或灰度），并使用给定的命令行参数整数值应用缩减。在OpenCV中，目前有三种主要的方式逐像素地浏览图像。为了使事情更有趣一点，我们将使用这些方法中的每一种对图像进行扫描，并打印出所花的时间。</p>
<p>您可以下载完整的源代码<a href="https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp">在这里</a>或者在OpenCV的samples目录中的cpp教程代码的核心部分进行查找。其基本用途是：</p><div class="fragment"><div class="line">how_to_scan_images imageName.jpg intValueToReduce [G]</div></div><!-- fragment --><p>最后一个参数是可选的。如果给定，图像将以灰度格式加载，否则使用BGR颜色空间。第一件事是计算查找表。</p>
<div class="fragment"><div class="line">    <span class="keywordtype">int</span> divideWith = 0; <span class="comment">// convert our input string to number - C++ style</span></div><div class="line">    stringstream s;</div><div class="line">    s &lt;&lt; argv[2];</div><div class="line">    s &gt;&gt; divideWith;</div><div class="line">    <span class="keywordflow">if</span> (!s || !divideWith)</div><div class="line">    {</div><div class="line">        cout &lt;&lt; <span class="stringliteral">&quot;Invalid number entered for dividing. &quot;</span> &lt;&lt; endl;</div><div class="line">        <span class="keywordflow">return</span> -1;</div><div class="line">    }</div><div class="line"></div><div class="line">    <a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga65f85814a8290f9797005d3b28e7e5fc">uchar</a> table[256];</div><div class="line">    <span class="keywordflow">for</span> (<span class="keywordtype">int</span> i = 0; i &lt; 256; ++i)</div><div class="line">       table[i] = (<a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga65f85814a8290f9797005d3b28e7e5fc">uchar</a>)(divideWith * (i/divideWith));</div></div><!-- fragment --><p>这里我们首先使用C++<em>stringstream</em>类将第三个命令行参数从文本转换为整数格式。然后我们使用一个简单的look和上面的公式来计算查找表。这里没有OpenCV特定的东西。</p>
<p>另一个问题是我们如何测量时间？OpenCV提供了两个简单的函数来实现这一点<a class="el" href="../../db/de0/group__core__utils.html#gae73f58000611a1af25dd36d496bf4487" title="Returns the number of ticks. ">cv::getTickCount()</a>和<a class="el" href="../../db/de0/group__core__utils.html#ga705441a9ef01f47acdc55d87fbe5090c" title="Returns the number of ticks per second. ">cv::getTickFrequency()</a>. 第一个返回某个事件中系统CPU的滴答数(比如自系统启动以来):第二个返回CPU在一秒钟内发出滴答声的次数:因此，测量两个操作之间经过的时间量非常简单:</p><div class="fragment"><div class="line"><span class="keywordtype">double</span> t = (double)<a class="code" href="../../db/de0/group__core__utils.html#gae73f58000611a1af25dd36d496bf4487">getTickCount</a>();</div><div class="line"><span class="comment">// do something ...</span></div><div class="line">t = ((double)<a class="code" href="../../db/de0/group__core__utils.html#gae73f58000611a1af25dd36d496bf4487">getTickCount</a>() - t)/<a class="code" href="../../db/de0/group__core__utils.html#ga705441a9ef01f47acdc55d87fbe5090c">getTickFrequency</a>();</div><div class="line">cout &lt;&lt; <span class="stringliteral">&quot;Times passed in seconds: &quot;</span> &lt;&lt; t &lt;&lt; endl;</div></div><!-- fragment --><p><a class="anchor" id="tutorial_how_to_scan_images_storing"></a></p><h2>图像矩阵是如何存储在内存中的？</h2>
<p>你已经在我的书里读到了<a class="el" href="../../d6/d6d/tutorial_mat_the_basic_image_container.html">Mat - The Basic Image Container</a>教程矩阵的大小取决于使用的颜色系统。更准确地说，这取决于使用的通道数。在灰度图像的情况下，我们有如下内容：</p>
<div class="image">
<img src="../../tutorial_how_matrix_stored_1.png" alt="tutorial_how_matrix_stored_1.png">
</div>
<p>对于多通道图像，列包含与通道数相同的子列。例如，对于BGR颜色系统：</p>
<div class="image">
<img src="../../tutorial_how_matrix_stored_2.png" alt="tutorial_how_matrix_stored_2.png">
</div>
<p>请注意，通道的顺序是相反的：BGR而不是RGB。因为在许多情况下，内存足够大，可以以连续的方式存储行，所以行可以一个接一个地跟随，从而创建一个单独的长行。因为所有的东西都在一个地方，一个接一个的，这可能有助于加快扫描过程。我们可以使用<a class="el" href="../../d3/d63/classcv_1_1Mat.html#aa90cea495029c7d1ee0a41361ccecdf3" title="Reports whether the matrix is continuous or not. ">cv::Mat::isContinuous()</a>函数到<em>问</em>矩阵，如果是这样的话。继续下一节查找示例。</p>
<h2>有效的方法</h2>
<p>在性能方面，您无法击败经典的C风格操作符[]（指针）访问。因此，我们可以推荐的最有效的分配方法是：</p>
<div class="fragment"><div class="line">Mat&amp; ScanImageAndReduceC(Mat&amp; I, <span class="keyword">const</span> <a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga65f85814a8290f9797005d3b28e7e5fc">uchar</a>* <span class="keyword">const</span> table)</div><div class="line">{</div><div class="line">    <span class="comment">// accept only char type matrices</span></div><div class="line">    <a class="code" href="../../db/de0/group__core__utils.html#gaf62bcd90f70e275191ab95136d85906b">CV_Assert</a>(I.depth() == <a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga32b18d904ee2b1731a9416a8eef67d06">CV_8U</a>);</div><div class="line"></div><div class="line">    <span class="keywordtype">int</span> channels = I.channels();</div><div class="line"></div><div class="line">    <span class="keywordtype">int</span> nRows = I.rows;</div><div class="line">    <span class="keywordtype">int</span> nCols = I.cols * channels;</div><div class="line"></div><div class="line">    <span class="keywordflow">if</span> (I.isContinuous())</div><div class="line">    {</div><div class="line">        nCols *= nRows;</div><div class="line">        nRows = 1;</div><div class="line">    }</div><div class="line"></div><div class="line">    <span class="keywordtype">int</span> i,j;</div><div class="line">    <a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga65f85814a8290f9797005d3b28e7e5fc">uchar</a>* p;</div><div class="line">    <span class="keywordflow">for</span>( i = 0; i &lt; nRows; ++i)</div><div class="line">    {</div><div class="line">        p = I.ptr&lt;<a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga65f85814a8290f9797005d3b28e7e5fc">uchar</a>&gt;(i);</div><div class="line">        <span class="keywordflow">for</span> ( j = 0; j &lt; nCols; ++j)</div><div class="line">        {</div><div class="line">            p[j] = table[p[j]];</div><div class="line">        }</div><div class="line">    }</div><div class="line">    <span class="keywordflow">return</span> I;</div><div class="line">}</div></div><!-- fragment --><p>在这里，我们基本上只需要获取一个指向每一行开头的指针，然后遍历它直到它结束。在矩阵以连续方式存储的特殊情况下，我们只需要请求指针一次，一直到最后。我们需要注意彩色图像：我们有三个通道，所以我们需要通过每一行的三倍以上的项目。</p>
<p>还有另一种方法。这个<em>data</em>数据库的数据成员<em>Mat</em>对象返回指向第一行、第一列的指针。如果此指针为空，则该对象中没有有效的输入。检查这是检查图像加载是否成功的最简单方法。如果存储是连续的，我们可以用它遍历整个数据指针。如果是灰度图像，则如下所示：</p><div class="fragment"><div class="line"><a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga65f85814a8290f9797005d3b28e7e5fc">uchar</a>* p = I.data;</div><div class="line"></div><div class="line"><span class="keywordflow">for</span>( <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> i = 0; i &lt; ncol*nrows; ++i)</div><div class="line">    *p++ = table[*p];</div></div><!-- fragment --><p>你会得到同样的结果。但是，这段代码以后很难阅读。如果你有更先进的技术那就更难了。此外，在实践中，我观察到您将获得相同的性能结果（因为大多数现代编译器可能会自动为您实现这个小优化技巧）。</p>
<h2>迭代器（safe）方法</h2>
<p>以确保通过适当数量的<em>uchar</em>字段和跳过行之间可能出现的间隙是您的责任。迭代器方法被认为是一种更安全的方法，因为它从用户那里接管这些任务。你需要做的就是询问图像矩阵的开始和结束，然后增加开始迭代器直到你到达结束。获取价值<em>指出</em>由迭代器使用*运算符（在它之前添加它）。</p>
<div class="fragment"><div class="line">Mat&amp; ScanImageAndReduceIterator(Mat&amp; I, <span class="keyword">const</span> <a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga65f85814a8290f9797005d3b28e7e5fc">uchar</a>* <span class="keyword">const</span> table)</div><div class="line">{</div><div class="line">    <span class="comment">// accept only char type matrices</span></div><div class="line">    <a class="code" href="../../db/de0/group__core__utils.html#gaf62bcd90f70e275191ab95136d85906b">CV_Assert</a>(I.depth() == <a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga32b18d904ee2b1731a9416a8eef67d06">CV_8U</a>);</div><div class="line"></div><div class="line">    <span class="keyword">const</span> <span class="keywordtype">int</span> channels = I.channels();</div><div class="line">    <span class="keywordflow">switch</span>(channels)</div><div class="line">    {</div><div class="line">    <span class="keywordflow">case</span> 1:</div><div class="line">        {</div><div class="line">            MatIterator_&lt;uchar&gt; it, end;</div><div class="line">            <span class="keywordflow">for</span>( it = I.begin&lt;<a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga65f85814a8290f9797005d3b28e7e5fc">uchar</a>&gt;(), end = I.end&lt;<a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga65f85814a8290f9797005d3b28e7e5fc">uchar</a>&gt;(); it != end; ++it)</div><div class="line">                *it = table[*it];</div><div class="line">            <span class="keywordflow">break</span>;</div><div class="line">        }</div><div class="line">    <span class="keywordflow">case</span> 3:</div><div class="line">        {</div><div class="line">            MatIterator_&lt;Vec3b&gt; it, end;</div><div class="line">            <span class="keywordflow">for</span>( it = I.begin&lt;<a class="code" href="../../dc/d84/group__core__basic.html#ga7e6060c0b8d48459964df6e1eb524c03">Vec3b</a>&gt;(), end = I.end&lt;<a class="code" href="../../dc/d84/group__core__basic.html#ga7e6060c0b8d48459964df6e1eb524c03">Vec3b</a>&gt;(); it != end; ++it)</div><div class="line">            {</div><div class="line">                (*it)[0] = table[(*it)[0]];</div><div class="line">                (*it)[1] = table[(*it)[1]];</div><div class="line">                (*it)[2] = table[(*it)[2]];</div><div class="line">            }</div><div class="line">        }</div><div class="line">    }</div><div class="line"></div><div class="line">    <span class="keywordflow">return</span> I;</div><div class="line">}</div></div><!-- fragment --><p>对于彩色图像，我们每列有三个uchar项。这可能被认为是uchar项目的一个短向量，它已经在OpenCV中用<em>Vec3b</em>姓名。要访问第n个子列，我们使用简单运算符[]访问。重要的是要记住OpenCV迭代器遍历列并自动跳到下一行。因此在彩色图像的情况下，如果你使用一个简单的<em>uchar</em>迭代器只能访问蓝色通道值。</p>
<h2>引用返回的动态地址计算</h2>
<p>最后一种方法不建议用于扫描。它被用来获取或修改图像中的随机元素。它的基本用法是指定要访问的项的行号和列号。在我们早期的扫描方法中，您可能已经注意到，通过我们查看图像的类型是很重要的。这里没有什么不同，因为您需要手动指定在自动查找时使用什么类型。您可以在灰度图像的情况下观察以下源代码（使用+<a class="el" href="../../d3/d63/classcv_1_1Mat.html#aa5d20fc86d41d59e4d71ae93daee9726" title="Returns a reference to the specified array element. ">cv::Mat::at()</a>功能）：</p>
<div class="fragment"><div class="line">Mat&amp; ScanImageAndReduceRandomAccess(Mat&amp; I, <span class="keyword">const</span> <a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga65f85814a8290f9797005d3b28e7e5fc">uchar</a>* <span class="keyword">const</span> table)</div><div class="line">{</div><div class="line">    <span class="comment">// accept only char type matrices</span></div><div class="line">    <a class="code" href="../../db/de0/group__core__utils.html#gaf62bcd90f70e275191ab95136d85906b">CV_Assert</a>(I.depth() == <a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga32b18d904ee2b1731a9416a8eef67d06">CV_8U</a>);</div><div class="line"></div><div class="line">    <span class="keyword">const</span> <span class="keywordtype">int</span> channels = I.channels();</div><div class="line">    <span class="keywordflow">switch</span>(channels)</div><div class="line">    {</div><div class="line">    <span class="keywordflow">case</span> 1:</div><div class="line">        {</div><div class="line">            <span class="keywordflow">for</span>( <span class="keywordtype">int</span> i = 0; i &lt; I.rows; ++i)</div><div class="line">                <span class="keywordflow">for</span>( <span class="keywordtype">int</span> j = 0; j &lt; I.cols; ++j )</div><div class="line">                    I.at&lt;<a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga65f85814a8290f9797005d3b28e7e5fc">uchar</a>&gt;(i,j) = table[I.at&lt;<a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga65f85814a8290f9797005d3b28e7e5fc">uchar</a>&gt;(i,j)];</div><div class="line">            <span class="keywordflow">break</span>;</div><div class="line">        }</div><div class="line">    <span class="keywordflow">case</span> 3:</div><div class="line">        {</div><div class="line">         Mat_&lt;Vec3b&gt; _I = I;</div><div class="line"></div><div class="line">         <span class="keywordflow">for</span>( <span class="keywordtype">int</span> i = 0; i &lt; I.rows; ++i)</div><div class="line">            <span class="keywordflow">for</span>( <span class="keywordtype">int</span> j = 0; j &lt; I.cols; ++j )</div><div class="line">               {</div><div class="line">                   _I(i,j)[0] = table[_I(i,j)[0]];</div><div class="line">                   _I(i,j)[1] = table[_I(i,j)[1]];</div><div class="line">                   _I(i,j)[2] = table[_I(i,j)[2]];</div><div class="line">            }</div><div class="line">         I = _I;</div><div class="line">         <span class="keywordflow">break</span>;</div><div class="line">        }</div><div class="line">    }</div><div class="line"></div><div class="line">    <span class="keywordflow">return</span> I;</div><div class="line">}</div></div><!-- fragment --><p>该函数接受您的输入类型和坐标，并计算查询项的地址。然后返回对它的引用。这可能是一个常数，当你<em>得到</em>当你<em>设置</em>价值观。作为安全措施<b>仅调试模式</b>*检查输入坐标是否有效且确实存在。如果不是这样的话，您将在标准错误输出流中得到一个很好的输出消息。与release模式中的有效方法相比，使用这种方法的唯一区别在于，对于图像的每个元素，您将获得一个新的行指针，我们使用C操作符[]获取列元素。</p>
<p>如果您需要使用此方法对一个图像进行多次查找，那么为每个访问输入类型和at关键字可能会非常麻烦和耗时。为了解决这个问题，OpenCV有一个<a class="el" href="../../df/dfc/classcv_1_1Mat__.html" title="Template matrix class derived from Mat. ">cv::Mat_</a>数据类型。它与Mat相同，只是在定义时需要通过查看数据矩阵的内容来指定数据类型，但是作为回报，您可以使用operator（）快速访问项。为了使事情变得更好，这是很容易转换和正常<a class="el" href="../../d3/d63/classcv_1_1Mat.html" title="n-dimensional dense array class ">cv::Mat</a>数据类型。您可以在上面函数的彩色图像中看到这个函数的示例用法。尽管如此，重要的是要注意，相同的操作（具有相同的运行时速度）可以用<a class="el" href="../../d3/d63/classcv_1_1Mat.html#aa5d20fc86d41d59e4d71ae93daee9726" title="Returns a reference to the specified array element. ">cv::Mat::at</a>功能。对于懒惰的程序员来说，这只是一个少写的把戏。</p>
<h2>核心功能</h2>
<p>这是在图像中实现查找表修改的额外方法。在图像处理中，很常见的情况是要将所有给定的图像值修改为其他值。OpenCV提供了一个修改图像值的函数，无需编写图像的扫描逻辑。我们使用<a class="el" href="../../d2/de8/group__core__array.html#gab55b8d062b7f5587720ede032d34156f" title="Performs a look-up table transform of an array. ">cv::LUT()</a>核心模块的功能。首先，我们构建查找表的Mat类型：</p>
<div class="fragment"><div class="line">    Mat lookUpTable(1, 256, <a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga32b18d904ee2b1731a9416a8eef67d06">CV_8U</a>);</div><div class="line">    <a class="code" href="../../d1/d1b/group__core__hal__interface.html#ga65f85814a8290f9797005d3b28e7e5fc">uchar</a>* p = lookUpTable.ptr();</div><div class="line">    <span class="keywordflow">for</span>( <span class="keywordtype">int</span> i = 0; i &lt; 256; ++i)</div><div class="line">        p[i] = table[i];</div></div><!-- fragment --><p>最后调用函数（I是我们的输入图像，J是输出图像）：</p>
<div class="fragment"><div class="line">        <a class="code" href="../../d2/de8/group__core__array.html#gab55b8d062b7f5587720ede032d34156f">LUT</a>(I, lookUpTable, J);</div></div><!-- fragment --> <h2>性能差异</h2>
<p>为了获得最佳结果，请编译程序并自己运行。为了使差异更清楚，我使用了一个相当大的（2560x1600）图像。这里呈现的性能是针对彩色图像的。为了得到一个更精确的值，我将调用函数得到的值平均了一百次。</p>
<table class="doxtable">
<tr>
<th>Method</th><th>时间</th></tr>
<tr>
<td>有效途径</td><td>79.4717毫秒</td></tr>
<tr>
<td>迭代器</td><td>83.7201毫秒</td></tr>
<tr>
<td>飞行中RA</td><td>93.7878毫秒</td></tr>
<tr>
<td>LUT函数</td><td>32.5759毫秒</td></tr>
</table>
<p>我们可以得出几个结论。如果可能的话，可以使用OpenCV已经生成的函数（而不是重新创建这些函数）。最快的方法是LUT函数。这是因为OpenCV库通过Intel线程构建块启用了多线程。但是，如果您需要编写一个简单的图像扫描首选指针方法。迭代器是一个更安全的赌注，但是速度相当慢。在调试模式下，使用实时参考访问方法进行全图像扫描是成本最高的。在发布模式中，它可能会击败迭代器方法，也可能不会，但是它肯定会为此牺牲迭代器的安全特性。</p>
<p>最后，您可以在<a href="https://www.youtube.com/watch?v=fB3AN5fjgwc">视频已发布</a>在我们的YouTube频道。</p>
<div align="center"><iframe title="Video" width="560" height="349" src="https://www.youtube.com/embed/fB3AN5fjgwc?rel=0" frameborder="0" align="middle" allowfullscreen=""></iframe></div> </div></div><!-- contents -->
<!-- HTML footer for doxygen 1.8.6-->
<!-- start footer part -->
<hr class="footer"><address class="footer"><small>Generated on Fri Apr 2 2021 11:36:34 for OpenCV by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="../../doxygen.png" alt="doxygen">
</a> 1.8.13
</small></address>
<script type="text/javascript">
//<![CDATA[
addTutorialsButtons();
//]]>
</script>
</body>
</html>
